 using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
using BriansClimb;
using BriansClimb.GameObjects;
using BriansClimb.Climber;
using BriansClimb.DrawableGameObjects.Buttons;
using BriansClimb.DrawableGameObjects.ClimbingHolds;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Collisions;


namespace BriansClimb.DrawableGameObjects.GameScenes
{
    /// <summary>
    /// This is a scene that allows the player to customize their own climbing
    /// wall.  It inherits GameScene.
    /// </summary>
    public class WallCustomizeScene : GameScene
    {
        protected Texture2D background;
        //!Texture to be used when the current hold is placed.
        protected Texture2D currentHold;
        protected Texture2D initialSaveButton;
        protected Texture2D pressedSaveButton;
        //!Texture used for the tool bar on the bottom.
        protected Texture2D sceneToolBar;
        protected SpriteBatch spriteBatch = null;
        protected GamePadState oldGamePadButtonState;

        protected Game game;
        public WallManager wallManager;
        //!Text menu component that shows when the player has reached the limit on placing holds.
        protected TextMenuComponent wallLimitWarning;
        protected TextMenuComponent escMenu;
        //!Toggle button for the rest hold.
        protected ToggleButton restButton;
        //!Toggle button for the jug hold.
        protected ToggleButton jugButton;
        //!Toggle button for the foot hold.
        protected ToggleButton footHoldButton;
        //!Toggle button for the crimp hold.
        protected ToggleButton crimpButton;
        //!Toggle button for the sloper hold.
        protected ToggleButton slopeButton;
        //!New button that resets the wall.
        protected LeftClickButton newButton;
        protected FileManager fileManager;
        protected List<ToggleButton> toggleButtonList;

        protected FileStatManager fileStatManager;
        //!Slider bar that lets the user choose the coefficient of friction.
        protected SliderBar coefBar;
        //!Initial coefficition value.
        protected string coefValue = " ";
        //!Used for checking for sound cues.
        protected int coef;
        //!Used for checking sound cues.    
        protected int oldCoef;
        //!Indicates the first run of the wallcustomize scene.
        protected bool first=true;
        protected SpriteFont normalFont;

        protected AudioManager audioManager;
        

        protected StaminaBar staminaBar;        

        //!The variable to hold the position of the climbing hold 
        protected Vector2 holdPosition;

        //!The x coordinate of the 'tile' the mouse is in
        protected int tileX;
        //!The y coordinate of the 'tile' the mouse is in
        protected int tileY;
        protected Game briansClimb;

        //!The number of climbing holds currently placed on the wall
        protected int climbingHoldCount;

        //!The predefined width of the tile.
        protected int tileWidth = 30;
        //!The predefined height of the tile.
        protected int tileHeight = 30;

        protected bool clicked;
        protected bool pressed;

        //!The BodyPart for the 'floor'
        protected BodyPart floor;


        //!Holds the current mouse state
        protected MouseState mouseCurrentState;

        //!Holds the previous keyboard state.
        protected KeyboardState oldKeyboardState;
        //!Holds the current keyboard state.
        protected KeyboardState keyboardState;

        //!Tint for when game is not paused.
        protected Color normalColor = Color.White;
        //!Tint for when game is paused.
        protected Color pausedColor = Color.Gray;
        protected Color theColor;
       
        //!Master Physics Simulator
        protected PhysicsSimulator physicsSimulator;

        //!Climber
        BriansClimb.Climber.Climber climber;
        
        //!Body part info handler.
        GameObjects.BodyPartInfoHandler bpih;


        /// <summary>
        /// Constructor for the WallCustomizeScene.
        /// </summary>
        /// <param name="game">Master Game object.</param>
        /// <param name="textMenuComponent">Text menu component.</param>
        /// <param name="fileManager">Persistent file manager.</param>
        /// <param name="fileStatManager">Persistent filestat manager.</param>
        /// <param name="background">Background texture</param>
        /// <param name="normalFont">Normal font for the text menu component.</param>
        /// <param name="selectedFont">Selected font for the text menu component.</param>
        /// <param name="physicsSimulatorIn">Master physics simulator.</param>
        public WallCustomizeScene(Game game, TextMenuComponent textMenuComponent,FileManager fileManager, FileStatManager fileStatManager,
                    Texture2D background, SpriteFont normalFont, SpriteFont selectedFont, PhysicsSimulator physicsSimulatorIn)
            : base(game)
        {
            //gets the current sprite batch
            spriteBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
            audioManager = (AudioManager)game.Services.GetService(typeof(AudioManager));

            //gets the current keyboard state
            oldKeyboardState = Keyboard.GetState();
            this.fileStatManager = fileStatManager;
            this.game = game;
            this.fileManager = fileManager;
            this.background = background;
            this.normalFont = normalFont;
            escMenu = textMenuComponent;
            toggleButtonList = new List<ToggleButton>();
            sceneToolBar = game.Content.Load<Texture2D>(".\\Textures\\SceneToolBar");
            oldGamePadButtonState = GamePad.GetState(PlayerIndex.One);

            //creating the buttons
            restButton = new ToggleButton(game, "rest", 300, 955);
            jugButton = new ToggleButton(game, "jug", 380, 955);
            slopeButton = new ToggleButton(game, "slope", 460, 955);
            crimpButton = new ToggleButton(game, "crimp", 540, 955);
            footHoldButton = new ToggleButton(game, "footHold", 620, 955);

            //adding toggle buttons to a list
            toggleButtonList.Add(restButton);
            toggleButtonList.Add(jugButton);
            toggleButtonList.Add(slopeButton);
            toggleButtonList.Add(crimpButton);
            toggleButtonList.Add(footHoldButton);

            //adding to wallcustomize scene's component list
            Components.Add(restButton);
            Components.Add(jugButton);
            Components.Add(slopeButton);
            Components.Add(crimpButton);
            Components.Add(footHoldButton);

            //creating the friction slider bar
            coefBar = new SliderBar(game, 400, 5, new Vector2(720, 972));
            Components.Add(coefBar);
            
            //creating 'new' button
            newButton = new LeftClickButton(game, "new", 100, 959);
            Components.Add(newButton);
         
            //creating the warning text
            wallLimitWarning = new TextMenuComponent(game, normalFont, selectedFont);

            //set text for warning
            string[] warningText = { "Climbing Hold Limit Reached" };
            wallLimitWarning.SetMenuItems(warningText);
            
            //adding to wallcustomize scene's component list
            Components.Add(wallLimitWarning);            

            //physicsSimulator = new PhysicsSimulator(new FarseerGames.FarseerPhysics.Mathematics.Vector2(0, 0));
            this.physicsSimulator = physicsSimulatorIn;
            bpih = new BodyPartInfoHandler(game);

            //creating the wall manager
            wallManager = new WallManager(game, this, this.physicsSimulator, bpih, Components);
            Components.Add(wallManager);

            float staminaBarValue = fileStatManager.StaminaBarValue;           
         }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here
            
            base.Initialize();
        }

        /// <summary>
        /// Property that represents the New Button Object.
        /// </summary>
        public LeftClickButton NewButton
        {
            get { return newButton; }
        }

        /// <summary>
        /// Property that represents the Friction Slider.
        /// </summary>
        public SliderBar FrictionSlider
        {
            get { return coefBar; }
        }

        /// <summary>
        /// Property that represents the Rest Hold button.
        /// </summary>
        public ToggleButton RestButton
        {
            get { return restButton; }
        }

        /// <summary>
        /// Property that represents the Jub Hold button.
        /// </summary>
        public ToggleButton JugButton
        {
            get { return jugButton; }
        }

        /// <summary>
        /// Property that represents the Foothold Toggle Button Object.
        /// </summary>
        public ToggleButton FootHoldButton
        {
            get { return footHoldButton; }
        }

        /// <summary>
        /// Property that represents the Crimp Toggle Button Object.
        /// </summary>
        public ToggleButton CrimpHoldButton
        {
            get { return crimpButton; }
        }

        /// <summary>
        /// Property that represents the Slope Toggle Button Object.
        /// </summary>
        public ToggleButton SlopeHoldButton
        {
            get { return slopeButton; }
        }

        /// <summary>
        /// Property that represents the list of Toggle Buttons.
        /// </summary>
        public List<ToggleButton> ToggleButtonList
        {
            get { return toggleButtonList; }
        }

        /// <summary>
        /// Property that represents the warning text object
        /// </summary>
        public TextMenuComponent WarningText
        {
            get { return wallLimitWarning; }
        }

        /// <summary>
        /// Property that represents the 'esc' menu object
        /// </summary>
        public TextMenuComponent EscMenu
        {
            get { return escMenu; }
        }

        /// <summary>
        /// Property that represents the current list of climbing holds
        /// </summary>
        public List<ClimbingHold> WallList
        {
            get { return wallManager.ClimbingHoldList; }
        }

        /// <summary>
        /// Property that represents the selected menu option for the warning text
        /// </summary>
        public int WarningSelectedMenuIndex
        {
            get { return wallLimitWarning.SelectedItemIndex; }
        }

        /// <summary>
        /// Property that represents the selected menu option for the 'esc' menu
        /// </summary>
        public int EscSelectedMenuIndex
        {
            get { return escMenu.SelectedItemIndex; }
            set { escMenu.SelectedItemIndex = value; }
        }

        /// <summary>
        /// Property that represents the physics simulator.
        /// </summary>
        public PhysicsSimulator PhysSimulator
        {
            get { return physicsSimulator; }
        }

        /// <summary>
        /// Property that represents the body part info handler.
        /// </summary>
        public BodyPartInfoHandler BPIH
        {
            get { return bpih; }
            set { bpih = value; }
        }

        /// <summary>
        /// Shows the Wall Customize Scene
        /// </summary>
        public override void Show()
        {

            //sets the initial state of the warning text popup to hidden
            wallLimitWarning.Hide();

            //sets the initial state of the 'esc' menu to hidden
            escMenu.Hide();

            //color of background is normal white tint
            theColor = normalColor;

            base.Show();
        }

        /// <summary>
        /// Hides the Wall Customize Scene
        /// </summary>
        public override void Hide()
        {
            //makes sure the 'esc' menu is not showing
            escMenu.Hide();

            //makes sure the warning text is not showing
            wallLimitWarning.Hide();

            base.Hide();
        }

        /// <summary>
        /// Shows the 'esc' menu.
        /// </summary>
        public void ShowEscMenu()
        {
            //creating the 'esc' menu
            string[] menuItems = { "Resume", "Main Menu", "Save", "Load" };
            escMenu.SetMenuItems(menuItems);

            // Put the menu centered in screen
            escMenu.Position = new Vector2((Game.Window.ClientBounds.Width -
                                          escMenu.Width) / 2, 300);

            //set the 'esc' menu to be active
            escMenu.Show();

            //warning text hides when 'esc' menu is shown
            wallLimitWarning.Hide();

            //when scene is paused, background has grey tint
            theColor = pausedColor;

            base.Pause();
        }

        /// <summary>
        /// Shows the climbing hold limit warning text and pauses
        /// the wall customize scene.
        /// </summary>
        public void ShowWarning()
        {

            // Put the menu centered in screen
            wallLimitWarning.Position = new Vector2((Game.Window.ClientBounds.Width -
                                          wallLimitWarning.Width) / 2, 300);

            //show the warning text popup
            wallLimitWarning.Show();

            base.Pause();
        }

        /// <summary>
        /// Checks for the left mouse button click(press & release).
        /// </summary>
        private void CheckButtonClick()
        {
            mouseCurrentState = Mouse.GetState();
            clicked = false;

            if (mouseCurrentState.LeftButton == ButtonState.Pressed)
                pressed = true;

            if (mouseCurrentState.LeftButton == ButtonState.Released && pressed)
            {
                clicked = true;
                pressed = false;
            }
        }

        /// <summary>
        /// Checks for the 'esc' key click(press & release).
        /// </summary>
        /// <returns>True for a click, False otherwise</returns>
        private bool CheckEscMenuClick()
        {
            keyboardState = Keyboard.GetState();

            bool escResult = (oldKeyboardState.IsKeyDown(Keys.Escape) &&
                keyboardState.IsKeyUp(Keys.Escape));

            oldKeyboardState = keyboardState;

            return escResult;
        }

        /// <summary>
        /// Checks to see if the stamina bar has run out and indicates to the 
        /// climber to let go if it has.
        /// </summary>
        private void checkStamina()
        {
            //checking if the stamina bar has run out of stamina
            if (staminaBar.NoStamina)
                //lets go if it has
                climber.letGo();            
        }

        /// <summary>
        /// Handles playing a click sound when the friction slider bar is moved.
        /// </summary>
        private void handleSounds()
        {
            //if the current coefficient is not equal to the old coefficient
            if (coef != oldCoef)
                if (!first)
                    //play a sound
                    audioManager.PlayCue("btnclick5");
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            physicsSimulator.Update(gameTime.ElapsedGameTime.Milliseconds * .001f);
            
            //Update the coef value
            coefValue = (coefBar.getBarAttribute + 1).ToString();
            coef = coefBar.getBarAttribute;
            
            //Grab the mouse state
            mouseCurrentState = Mouse.GetState();

            //check for the 'esc' key press
            if (CheckEscMenuClick())
                ShowEscMenu();

            //Calculate X and Y coordinates
            tileX = (int)Math.Floor((float)(mouseCurrentState.X / tileWidth));
            tileY = (int)Math.Floor((float)(mouseCurrentState.Y / tileHeight));

            //Set the climbing hold to be drawn to a whole tile coordinate
            holdPosition.X = (tileX * tileWidth) + (tileWidth / 2);
            holdPosition.Y = (tileY * tileHeight) + (tileHeight / 2);

            //give the most recently placed hold position to the wall manager
            wallManager.SetClimbingHoldPosition(holdPosition);

            //handle the sounds for the scene
            handleSounds();            
            
            //set the old coefficient to the new one
            oldCoef = coef;
            
            //set the first flag to false
            if (first)
                first = !first;
            base.Update(gameTime);
        }

        /// <summary>
        /// Allows the game component to draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Draw(GameTime gameTime)
        {
            //draw the background filling the whole screen
            spriteBatch.Draw(background, new Rectangle(0, 0, Game.Window.ClientBounds.Width,
                                Game.Window.ClientBounds.Height), theColor);

            //draw the tool bar on the bottom of the screen
            spriteBatch.Draw(sceneToolBar, new Rectangle(6, 950, 1268, 68), Color.White);
            
            //draw the string that represents the coefficient value
            spriteBatch.DrawString(normalFont, coefValue, new Vector2(1135, 963), Color.Black);

            base.Draw(gameTime);
        }

    }
}